﻿using System;
using System.Runtime.CompilerServices;

namespace RayDen.Library.Core.Primitives
{
    public struct TriangleDataInfo
    {
        public MeshVertexInfo v0, v1, v2;


        public TriangleInfo GetInfo()
        {
            return new TriangleInfo(v0.VertexIndex, v1.VertexIndex, v2.VertexIndex);
        }

        public AABB WorldBound(Point[] vertices)
        {
            var pts = vertices;
            var res = new AABB(pts[this.v0.VertexIndex], pts[this.v1.VertexIndex]);
            res.Union(pts[v2.VertexIndex]);
            return res;
        }

        public Point Center(Point[] vertices)
        {
            var v = WorldBound(vertices).Center;
            return v;
        }

        public Vector Sum(Point[] vertices)
        {
            var v = WorldBound(vertices).Sum;
            return v;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Normal ComputeNormal(Point[] vertices)
        {
            var n = (Normal)((vertices[v0.VertexIndex] - vertices[v1.VertexIndex]) ^ (vertices[v0.VertexIndex] - vertices[v2.VertexIndex]));
            if (n.Length > 0f)
                n.Normalize();
            return n;
            //return (Normal)((vertices[v0] - vertices[v1]) ^ (vertices[v0] - vertices[v2]));
        }

        public Normal ComputeVerticeNormal(int vInd, Vector[] meshVertices)
        {
            Vector v_0, v_1, v_2;

            switch (vInd)
            {
                case 0:
                    v_0 = meshVertices[v0.VertexIndex];
                    v_1 = meshVertices[v1.VertexIndex];
                    v_2 = meshVertices[v2.VertexIndex];
                    break;
                case 1:
                    v_0 = meshVertices[v1.VertexIndex];
                    v_1 = meshVertices[v0.VertexIndex];
                    v_2 = meshVertices[v2.VertexIndex];
                    break;
                case 2:
                    v_0 = meshVertices[v2.VertexIndex];
                    v_1 = meshVertices[v1.VertexIndex];
                    v_2 = meshVertices[v0.VertexIndex];
                    break;
                default:
                    throw new ArgumentException();
            }
            var Normal = (Normal)((v_1 - v_0) ^ (v_2 - v_0)).Normalize();
            //var Normal = (Normal)((v_0 - v_1) ^ (v_0 - v_2)).Normalize();

            return Normal;
        }
        public Normal ComputeVerticeNormal(int vInd, Point[] meshVertices)
        {
            Point v_0, v_1, v_2;

            switch (vInd)
            {
                case 0:
                    v_0 = meshVertices[v0.VertexIndex];
                    v_1 = meshVertices[v1.VertexIndex];
                    v_2 = meshVertices[v2.VertexIndex];
                    break;
                case 1:
                    v_0 = meshVertices[v1.VertexIndex];
                    v_1 = meshVertices[v0.VertexIndex];
                    v_2 = meshVertices[v2.VertexIndex];
                    break;
                case 2:
                    v_0 = meshVertices[v2.VertexIndex];
                    v_1 = meshVertices[v1.VertexIndex];
                    v_2 = meshVertices[v0.VertexIndex];
                    break;
                default:
                    throw new ArgumentException();
            }
            //var Normal = (Normal)((v_1 - v_0) ^ (v_2 - v_0)).Normalize();
            var Normal = (Normal)((v_0 - v_1) ^ (v_0 - v_2)).Normalize();

            return Normal;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public float AreaV(Point[] meshVertices)
        {
            var verts = meshVertices;
            var p0 = verts[v0.VertexIndex];
            var p1 = verts[v1.VertexIndex];
            var p2 = verts[v2.VertexIndex];


            return 0.5f * ((p1 - p0) ^ (p2 - p0)).Length;
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Sample(Point[] meshVertices, float u0, float u1, out Point samplePoint, out float b0, out float b1, out float b2)
        {
            b0 = 0f;
            b1 = 0f;
            MC.UniformSampleTriangle(u0, u1, ref  b0, ref b1);

            var p0 = meshVertices[v0.VertexIndex];
            var p1 = meshVertices[v1.VertexIndex] - p0;
            var p2 = meshVertices[v2.VertexIndex] - p0;
            b2 = 1f - (b0) - (b1);


            samplePoint = new Point(
                ((b0) * p0.x + (b1) * p1.x + (b2) * p2.x),
                ((b0) * p0.y + (b1) * p1.y + (b2) * p2.y),
                ((b0) * p0.z + (b1) * p1.z + (b2) * p2.z));
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]

        public static void Sample(ref TriangleDataInfo tri,Point[] meshVertices, float u0, float u1, out Point samplePoint, out float b0, out float b1, out float b2)
            {
            b0 = 0f;
            b1 = 0f;
            MC.UniformSampleTriangle(u0, u1, ref  b0, ref b1);

            var p0 = meshVertices[tri.v0.VertexIndex];
            var p1 = meshVertices[tri.v1.VertexIndex] - p0;
            var p2 = meshVertices[tri.v2.VertexIndex] - p0;
            b2 = 1f - (b0) - (b1);


            samplePoint = new Point(
                ((b0) * p0.x + (b1) * p1.x + (b2) * p2.x),
                ((b0) * p0.y + (b1) * p1.y + (b2) * p2.y),
                ((b0) * p0.z + (b1) * p1.z + (b2) * p2.z));
        }
        public Normal InterpolateNormal(Point[] meshV, float b1, float b2)
        {
            var n0 = this.ComputeVerticeNormal(0, meshV);
            var n1 = this.ComputeVerticeNormal(1, meshV);
            var n2 = this.ComputeVerticeNormal(2, meshV);
            float b0 = 1f - b1 - b2;
            return (b0 * n0 + b1 * n1 + b2 * n2).Normalize();
        }
    }
}